﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using SimpleNotify.Service.Contracts;
using System.ServiceModel;

namespace SimpleNotify.Service
{
    public class SubscriptionManager
    {
        private Dictionary<string, INotifyCallbackContract> _subscribers;
        private readonly object subscriberListLock = new object();
        private Logger _logger;

        public SubscriptionManager(Logger logger)
        {
            _logger = logger;
            _subscribers = new Dictionary<string, INotifyCallbackContract>();
        }

        internal void UpdateSubscription(string machineName, INotifyCallbackContract callerProxy)
        {
            lock (subscriberListLock)
            {
                if (!_subscribers.ContainsKey(machineName.ToUpper()))
                {
                    _subscribers.Add(machineName.ToUpper(), callerProxy);

                    _logger.Write("New subscriber: " + machineName);
                    _logger.Write("Total subscribers: " + _subscribers.Count);
                }
                else if (_subscribers[machineName.ToUpper()] != callerProxy)
                {
                    IContextChannel c = (IContextChannel)_subscribers[machineName.ToUpper()];
                    c.Abort();
                    c.Close();

                    _subscribers[machineName.ToUpper()] = callerProxy;
                    _logger.Write("Subscriber updated: " + machineName);
                    _logger.Write("Total subscribers: " + _subscribers.Count);
                }
            }
        }

        internal string[] GetSubscribers()
        {
            lock (subscriberListLock)
            {
                return _subscribers.Keys.ToArray();
            }
        }

        internal bool ContainsSubscriber(string clientToNotify)
        {
            lock (subscriberListLock)
            {
                return _subscribers.ContainsKey(clientToNotify.ToUpper());
            }
        }

        internal INotifyCallbackContract GetSubscriber(string client)
        {
            lock (subscriberListLock)
            {
                return _subscribers[client];
            }
        }

        internal void RemoveSubscriber(string ClientHostname)
        {
            lock (subscriberListLock)
            {
                _subscribers.Remove(ClientHostname.ToUpper());
            }

            _logger.Write("Subscriber removed: " + ClientHostname);
            _logger.Write("Total subscribers: " + _subscribers.Count);
        }

        internal PushResult NotifySubscriber(string title, string text, string client, NotificationType type, string data)
        {
            PushResult result = new PushResult();

            result.ClientHostname = client;

            if (!_subscribers.ContainsKey(client.ToUpper()))
            {
                result.Error = "No subscription for " + client + " found";
                _logger.Write("No subscription for " + client + " found");
            }
            else
            {
                if (text == null) text = String.Empty;
                if (title == null) title = String.Empty;
                try
                {
                    lock (subscriberListLock)
                    {
                        IContextChannel c = (IContextChannel)_subscribers[client.ToUpper()];
                        c.OperationTimeout = new TimeSpan(0, 0, 5);
                                                
                        result.NotificationReceived = _subscribers[client.ToUpper()].Notify(title, text, type, data);
                    }
                    _logger.Write("Notification sent -> " + client);
                }
                catch (TimeoutException)
                {
                    result.Error = "Could not notify client " + client + ": Timeout";
                    _logger.Write("Could not notify client " + client + ": Timeout");
                    RemoveSubscriber(client);
                }
                catch (Exception ex)
                {
                    result.Error = ex.Message;
                    _logger.Write("Could not notify client " + client + ": " + ex.ToString());
                    RemoveSubscriber(client);
                }
            }

            return result;
        }
    }
}